Skip to content

[DX-155]- Unifies start and end params for history commands#150

Merged
umair-ably merged 7 commits intomainfrom
DX-155-start-end-params
Mar 6, 2026
Merged

[DX-155]- Unifies start and end params for history commands#150
umair-ably merged 7 commits intomainfrom
DX-155-start-end-params

Conversation

@umair-ably
Copy link
Contributor

@umair-ably umair-ably commented Mar 5, 2026

Summary

Three related improvements across the CLI. The diff is large (~80 files) but most changes are mechanical.

Unified time range flags

History and stats commands each had their own --start/--end with inconsistent types. Now they all share timeRangeFlags from src/flags.ts and parse with parseTimestamp() from src/utils/time.ts, which accepts:

--start "2023-01-01T00:00:00Z" # ISO 8601
--start 1700000000000 # Unix ms
--start 1h # Relative (also: 30s, 5m, 2d, 1w)

Applied to: channels history, rooms messages history, logs history, logs connection-lifecycle history, logs push history, stats account, stats app.

Output consistency

Mechanical pass applying progress(), success(), resource() helpers everywhere they were missing. Also wrapped human output in shouldOutputJson() guards, switched chalk.green("Label:") to chalk.dim("Label:") for secondary labels, and ensured JSON mode emits structured errors instead of silently swallowing them.

Cleanup

  • Removed all ABLY_DEBUG_KEYS / DEBUG_TEST / stray console.error debug artifacts
  • Removed _ prefixed unused variables
  • Restored clientIdFlag on 6 commands that enter a space or create realtime connections (was accidentally dropped during the flag refactor)
  • Added missing duration flag to 4 subscribe/long-running commands
  • Replaced manual SIGINT handlers with waitUntilInterruptedOrTimeout() in logs/push/subscribe
  • Fixed shadowed variables, missing override keywords, process.exit -> this.exit

Some of this stuff should've been added in previous PRs but Claude is getting better at picking up patterns and finding these issues. I created an Agent Team with several agents to go through and find these so hopefully this should be the baseline now.

Summary by CodeRabbit

  • New Features

    • Adds flexible time-range flags (--start, --end) to history and stats commands (ISO 8601, Unix ms, relative like "1h").
    • Introduces duration flags (-D) for automatic command timeouts.
  • Improvements

    • Consistent, richer CLI output: progress/success styling, indexed timestamp formatting in history outputs.
    • JSON mode: structured error output and cleaner non-JSON logging.
    • Clarified flag descriptions and adjusted command flag surfaces for a more consistent UX.

@umair-ably umair-ably requested review from Copilot and sacOO7 March 5, 2026 22:15
@vercel
Copy link

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cli-web-cli Ready Ready Preview, Comment Mar 6, 2026 6:36am

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cd330d28-0a85-495a-87cd-de8e1dbc5bbb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Introduces flexible time-range flags and a parseTimestamp utility, broad refactor of CLI flag sets (productApiFlags/clientIdFlag), consistent output helpers (progress/resource/success), JSON-aware error/output handling, duration flags for long-running commands, debug log removals, and expanded tests for time parsing and histories.

Changes

Cohort / File(s) Summary
Time range & parsing
src/flags.ts, src/utils/time.ts, src/commands/channels/history.ts, src/commands/logs/.../history.ts, src/commands/rooms/messages/history.ts, src/commands/stats/*.ts
Adds timeRangeFlags and parseTimestamp(); replaces explicit start/end flags and Date parsing with parsed ms values; updates examples and output timestamp formatting.
Output helpers & styling
src/commands/accounts/login.ts, src/commands/apps/*.ts, src/commands/channels/batch-publish.ts, src/commands/channels/list.ts, src/commands/channels/occupancy/get.ts, src/commands/channels/presence/*.ts, src/commands/channels/publish.ts, src/commands/rooms/messages/*.ts, src/commands/rooms/occupancy/*.ts
Introduces/uses progress, resource, success, and formatTimestamp for consistent CLI messages and replaces many plain/chalk logs with these helpers.
Flag architecture migration
src/commands/*/{rooms,spaces,apps,queues,channels,...}.ts
Replaces many uses of command-specific globalFlags/ChatBaseCommand.globalFlags/SpacesBaseCommand.globalFlags with productApiFlags and selectively adds clientIdFlag; adds override to static metadata where applicable.
JSON-aware output & errors
src/commands/integrations/delete.ts, src/commands/queues/delete.ts, src/commands/spaces/*, src/commands/stats/*.ts, src/commands/support/ask.ts, etc.
Adds conditional JSON branches using shouldOutputJson() and formatJsonOutput()/jsonError() to return structured responses/errors and suppress human logs in --json mode.
Duration / long-run changes
src/commands/logs/channel-lifecycle/subscribe.ts, src/commands/logs/push/subscribe.ts, src/commands/spaces/locks/acquire.ts, various subscribe/enter commands
Adds duration flags across several subscribe/enter commands and replaces manual signal handling with waitUntilInterruptedOrTimeout() in some places.
Minor behavior / housekeeping
src/commands/apps/channel-rules/{create,delete}.ts, src/services/history-manager.ts, src/commands/interactive.ts
Fixes variable shadowing, removes numerous debug logging blocks, and silences non-critical history-manager load errors.
Tests updated/added
test/unit/utils/time.test.ts, test/unit/commands/*history*.test.ts, other unit/e2e test adjustments
Adds comprehensive tests for parseTimestamp() and time-range handling; updates tests to new output formats and JSON behaviors.
Docs & lint
.claude/CLAUDE.md, .cursor/rules/AI-Assistance.mdc, eslint.config.js
Documentation and rule updates reflecting time-range flags, JSON error patterns, and formatting conventions; adds ignore patterns to ESLint config.
Other UI text tweaks
many files (duration descriptions, trailing punctuation)
Shortens duration flag descriptions (removes “0 = run indefinitely”), minor message wording and punctuation changes across commands.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant TimeUtil as parseTimestamp()
  participant API as ControlAPI
  participant Output

  User->>CLI: run command with --start/--end
  CLI->>TimeUtil: parseTimestamp(start) / parseTimestamp(end)
  TimeUtil-->>CLI: startMs / endMs
  CLI->>API: request history(stats) with startMs/endMs
  API-->>CLI: history data (timestamps, items)
  CLI->>Output: formatTimestamp(items/*.timestamp) + resource/progress wrappers
  Output-->>User: rendered output (JSON or human)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • AndyTWF

Poem

🐇 I parsed the times both near and far,
With progress bars that gleam like star,
Flags tidied, JSON now sings clear,
Debug crumbs swept far from here.
The rabbit hops—history draws near!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: unifying start and end parameters across history commands, which aligns with the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch DX-155-start-end-params

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Unifies --start/--end time range handling across history/stats CLI commands by introducing shared timeRangeFlags and a new parseTimestamp() utility (ISO 8601 / Unix ms / relative). Also standardizes output formatting and JSON guarding across many commands, while removing debug artifacts and restoring/adding missing flags (e.g., clientIdFlag, duration).

Changes:

  • Added src/utils/time.ts (parseTimestamp) and updated commands to use shared timeRangeFlags for consistent time parsing.
  • Standardized output helpers (progress/success/resource), improved JSON guarding, and removed debug logging artifacts.
  • Added/adjusted tests to cover flexible timestamps and updated output expectations.

Reviewed changes

Copilot reviewed 80 out of 80 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
test/unit/utils/time.test.ts Adds unit coverage for parseTimestamp() (ms, ISO, relative, errors).
test/unit/commands/stats/app.test.ts Verifies stats:app accepts ISO/relative/Unix ms for time flags.
test/unit/commands/stats/account.test.ts Verifies stats:account accepts ISO/relative/Unix ms for time flags.
test/unit/commands/rooms/messages.test.ts Adds coverage for rooms messages history time range parsing.
test/unit/commands/queues/delete.test.ts Updates expectations to match new standardized output.
test/unit/commands/interactive.test.ts Removes stderr debug logging in tests.
test/unit/commands/interactive-sigint.test.ts Renames unused vars and adjusts timeout fallback behavior.
test/unit/commands/did-you-mean.test.ts Removes CI debug output and cleans variable usage.
test/unit/commands/channels/history.test.ts Adds coverage for start/end parsing (Unix ms / relative).
test/unit/commands/channels/batch-publish.test.ts Updates JSON-mode parsing and output assertions to match new guards/helpers.
test/unit/commands/apps/set-apns-p12.test.ts Updates success message expectation.
test/e2e/spaces/spaces-e2e.test.ts Removes conditional debug output in polling loop.
test/e2e/interactive/ctrl-c-behavior.test.ts Removes DEBUG_TEST logging and console error artifacts.
src/utils/time.ts Introduces parseTimestamp() supporting ISO/Unix ms/relative shorthand.
src/services/history-manager.ts Removes debug-only logging; keeps silent failure behavior.
src/flags.ts Adds shared timeRangeFlags for --start/--end.
src/commands/support/ask.ts Adds JSON output for successful responses and adjusts spinner behavior.
src/commands/stats/app.ts Switches to timeRangeFlags + parseTimestamp; standardizes progress output.
src/commands/stats/account.ts Switches to timeRangeFlags + parseTimestamp; standardizes progress output.
src/commands/spaces/members/subscribe.ts Restores product flags/clientId flag; adds duration flag; improves JSON error payload.
src/commands/spaces/members/enter.ts Restores product flags/clientId flag and updates duration description.
src/commands/spaces/locks/subscribe.ts Restores product flags/clientId flag; adds duration flag; adjusts output + JSON errors.
src/commands/spaces/locks/get.ts Restores product flags/clientId flag and cleans legacy comments/strings.
src/commands/spaces/locks/get-all.ts Restores product flags/clientId flag.
src/commands/spaces/locks/acquire.ts Restores product flags/clientId flag; adds duration flag.
src/commands/spaces/locations/subscribe.ts Restores product flags/clientId flag; updates error output routing.
src/commands/spaces/locations/set.ts Restores product flags/clientId flag; replaces process.exit with this.exit.
src/commands/spaces/locations/get-all.ts Restores product flags/clientId flag; refactors location extraction; adjusts error handling.
src/commands/spaces/locations.ts Uses product flags and adds override markers; removes unused parse result.
src/commands/spaces/list.ts Uses product flags (instead of base global flags).
src/commands/spaces/cursors/subscribe.ts Restores product flags/clientId flag; adds duration; JSON guards readiness signal.
src/commands/spaces/cursors/set.ts Restores product flags/clientId flag; adds duration; punctuation consistency.
src/commands/spaces/cursors/get-all.ts Restores product flags/clientId flag; changes output style; uses this.error on failures.
src/commands/rooms/typing/subscribe.ts Uses product flags; updates duration description.
src/commands/rooms/typing/keystroke.ts Uses product flags; adds duration flag.
src/commands/rooms/reactions/subscribe.ts Uses product flags; updates duration description.
src/commands/rooms/reactions/send.ts Uses product flags.
src/commands/rooms/presence/subscribe.ts Uses product flags and restores clientIdFlag; updates duration description.
src/commands/rooms/presence/enter.ts Uses product flags and restores clientIdFlag; removes early readiness log.
src/commands/rooms/occupancy/subscribe.ts Uses product flags; adds override markers; updates duration description.
src/commands/rooms/occupancy/get.ts Uses product flags; output formatting via resource().
src/commands/rooms/messages/subscribe.ts Uses product flags + clientIdFlag; standardizes progress and resource formatting.
src/commands/rooms/messages/send.ts Uses product flags + clientIdFlag; progress helper usage.
src/commands/rooms/messages/reactions/subscribe.ts Uses product flags; removes unused flags parameters.
src/commands/rooms/messages/reactions/send.ts Uses product flags; punctuation consistency.
src/commands/rooms/messages/reactions/remove.ts Uses product flags; punctuation consistency.
src/commands/rooms/messages/history.ts Uses timeRangeFlags + parseTimestamp; standardizes output.
src/commands/rooms/list.ts Uses product flags; uses resource() for room names/counts.
src/commands/queues/delete.ts Skips confirmation in JSON mode; emits structured JSON success payload.
src/commands/logs/subscribe.ts Standardizes duration description; removes unused fields.
src/commands/logs/push/subscribe.ts Adds duration; replaces manual signal handling with wait helper.
src/commands/logs/push/history.ts Adds timeRangeFlags + parseTimestamp; updates examples.
src/commands/logs/history.ts Adds timeRangeFlags + parseTimestamp; uses formatTimestamp output convention.
src/commands/logs/connection-lifecycle/subscribe.ts Standardizes duration/rewind descriptions; removes unused cleanup flag.
src/commands/logs/connection-lifecycle/history.ts Adds timeRangeFlags + parseTimestamp; uses formatTimestamp.
src/commands/logs/channel-lifecycle/subscribe.ts Adds duration flag and uses it in wait helper.
src/commands/interactive.ts Removes ABLY_DEBUG_KEYS debug logging blocks.
src/commands/integrations/delete.ts Skips confirmation in JSON mode; emits structured JSON success payload.
src/commands/channels/subscribe.ts JSON-guards readiness signal; adjusts parsing variable usage.
src/commands/channels/publish.ts Uses progress() helper for multi-publish progress text.
src/commands/channels/presence/subscribe.ts Changes secondary labels to chalk.dim.
src/commands/channels/presence/enter.ts Changes secondary labels to chalk.dim.
src/commands/channels/occupancy/subscribe.ts Changes secondary labels to chalk.dim.
src/commands/channels/occupancy/get.ts Adds override markers; improves label formatting with chalk.dim.
src/commands/channels/occupancy.ts Adds override markers on static fields.
src/commands/channels/list.ts Uses resource() for channel names.
src/commands/channels/history.ts Adds timeRangeFlags + parseTimestamp; uses formatTimestamp.
src/commands/channels/batch-publish.ts Standardizes progress/success output; improves per-channel result formatting.
src/commands/bench/subscriber.ts Standardizes duration description.
src/commands/auth/keys/create.ts Uses progress() helper for creation message.
src/commands/apps/update.ts Uses progress() helper and resource() for app id.
src/commands/apps/set-apns-p12.ts Uses progress()/success() helpers; updates success message.
src/commands/apps/delete.ts Uses progress() helper and resource() for app id.
src/commands/apps/create.ts Uses progress() helper and resource() for app name.
src/commands/apps/channel-rules/list.ts Uses ControlBaseCommand.globalFlags (correct flag set).
src/commands/apps/channel-rules/delete.ts Fixes shadowed appId variable initialization.
src/commands/apps/channel-rules/create.ts Fixes shadowed appId variable initialization.
src/commands/accounts/login.ts Uses progress() helper for browser/app creation messaging.
.cursor/rules/AI-Assistance.mdc Documents new output + flag conventions (duration, timeRangeFlags, JSON errors).
.claude/CLAUDE.md Documents flag categories and JSON/error/output conventions including timeRangeFlags.
Comments suppressed due to low confidence (1)

src/commands/support/ask.ts:148

  • In the catch block, --json mode still logs a human-readable failure message (including chalk styling) and then calls this.error(), which will pollute JSON output. Prefer using this.jsonError({ error: errorMsg, success: false }, flags) (or equivalent) when shouldOutputJson(flags) is true, and avoid any non-JSON logging in that branch so JSON consumers get a clean error object plus a non-zero exit code.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Further reinforces rules introduced in previous unification efforts
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
test/unit/commands/queues/delete.test.ts (1)

47-103: ⚠️ Potential issue | 🟠 Major

Add tests for the new --json deletion path

The command now has JSON-mode specific behavior (structured success payload and no interactive confirmation), but this suite only validates human-output deletion text. Please add a focused --json success test (and ideally a non---force + --json case proving no prompt path).

As per coding guidelines: "Update or add tests when new features or changes are made, ensuring all tests pass before deeming work complete."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/queues/delete.test.ts` around lines 47 - 103, Add tests
covering the new --json path for queues:delete: create one test that calls
runCommand(["queues:delete", mockQueueId, "--force", "--json"], ...) (and the
equivalent custom-app-id variant) mocking the same GET /v1/apps/... and DELETE
endpoints with nock, then parse stdout as JSON and assert the structured success
payload (e.g., contains a success flag/queue id/message) and that no interactive
prompt text is present; also add a second test that runs
runCommand(["queues:delete", mockQueueId, "--json"], ...) without --force to
confirm JSON-mode bypasses interactive confirmation (mock any confirmation GETs
if present) and still returns the structured JSON success response. Ensure tests
reuse existing helpers like getMockConfigManager(), createMockQueue(), and
runCommand to mirror the other tests.
src/commands/spaces/cursors/set.ts (1)

68-70: ⚠️ Potential issue | 🟠 Major

--duration contract is inconsistent with command guidelines

--duration currently behaves as “0 = immediate exit” in runtime logic, but command guidelines require “0 = run indefinitely”. Please align both description and behavior.

Proposed fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),
-      // Decide how long to remain connected
-      if (flags.duration === 0) {
-        // Give Ably a moment to propagate the cursor update before exiting so that
-        // subscribers in automated tests have a chance to receive the event.
-        await new Promise((resolve) => setTimeout(resolve, 600));
-
-        // In immediate exit mode, we don't keep the process alive beyond this.
-        this.exit(0);
-      }
+      // Decide how long to remain connected (0 = run indefinitely)
+      const waitDuration = flags.duration === 0 ? undefined : flags.duration;
...
-      const exitReason = await waitUntilInterruptedOrTimeout(flags.duration);
+      const exitReason = await waitUntilInterruptedOrTimeout(waitDuration);

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

Also applies to: 382-389

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/set.ts` around lines 68 - 70, Update the duration
flag declaration and runtime handling so the flag description and behavior match
the command guidelines: change the Flags.integer({ description: ... }) for the
"duration" flag to "Automatically exit after N seconds (0 = run indefinitely)"
and ensure its alias is char: "D"; then modify any runtime logic that currently
treats duration === 0 as "immediate exit" to instead treat 0 as "run
indefinitely" (i.e., skip applying a timeout when duration is 0). Apply the same
description and behavior change to the other duration flag declaration found
around the second occurrence (lines ~382-389) so both definitions and their
consumers consistently use 0 = run indefinitely.
src/commands/rooms/reactions/subscribe.ts (1)

32-39: ⚠️ Potential issue | 🟡 Minor

Missing clientIdFlag for subscribe command.

This command creates a realtime connection via createChatClient() and attaches to a room. Per the flag architecture guidelines, commands that create a realtime client or join a channel should include clientIdFlag to allow users to supply a client identity.

Proposed fix
 import { ChatBaseCommand } from "../../../chat-base-command.js";
-import { productApiFlags } from "../../../flags.js";
+import { productApiFlags, clientIdFlag } from "../../../flags.js";
 import { waitUntilInterruptedOrTimeout } from "../../../utils/long-running.js";
 static override flags = {
   ...productApiFlags,
+  ...clientIdFlag,
   duration: Flags.integer({

Based on learnings: "The clientIdFlag (--client-id) should be available on commands where a client identity is meaningful... ensure commands that perform identifiable actions... or otherwise require a client identity expose the flag consistently."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/reactions/subscribe.ts` around lines 32 - 39, The
subscribe command's static flags (in the class in subscribe.ts) currently only
merges productApiFlags and duration; add the shared clientIdFlag to these flags
so users can pass --client-id when the command calls createChatClient() and
joins a room. Update the flags block (the static override flags in the Subscribe
command) to include clientIdFlag alongside productApiFlags and duration, and
ensure any import or reference to clientIdFlag is present so the flag is
recognized.
src/commands/spaces/locations/subscribe.ts (1)

390-403: ⚠️ Potential issue | 🟠 Major

Fail fast when subscription setup fails

If this.space.locations.subscribe(...) throws, non-JSON mode currently only logs and then continues to wait, resulting in a hanging command with no active subscription.

Suggested fix
       } catch (error) {
         const errorMsg = `Error subscribing to location updates: ${error instanceof Error ? error.message : String(error)}`;
         this.logCliEvent(flags, "location", "subscribeError", errorMsg, {
           error: errorMsg,
           spaceName,
         });
         if (this.shouldOutputJson(flags)) {
           this.jsonError(
             { error: errorMsg, spaceName, status: "error", success: false },
             flags,
           );
         } else {
-          this.logToStderr(errorMsg);
+          this.error(errorMsg);
         }
+        return;
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locations/subscribe.ts` around lines 390 - 403, The catch
block around this.space.locations.subscribe currently logs the error (via
this.logCliEvent and either this.jsonError or this.logToStderr) but does not
terminate, causing the CLI to hang; update the catch to fail fast by exiting or
throwing after logging—e.g., after calling this.jsonError or this.logToStderr,
call this.exit(1) or rethrow the error so the command stops. Ensure you modify
the catch that wraps this.space.locations.subscribe and keep the existing
logging calls (this.logCliEvent, this.jsonError, this.logToStderr) unchanged,
only adding the exit/throw to terminate execution.
🟡 Minor comments (26)
src/commands/channels/presence/enter.ts-43-46 (1)

43-46: ⚠️ Potential issue | 🟡 Minor

Restore the canonical --duration description text

The new description omits the 0 behavior, which makes CLI semantics less clear. Please keep the standard wording.

Suggested patch
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/channels/presence/enter.ts` around lines 43 - 46, Update the
duration flag description to the canonical text: change the Flags.integer option
for the duration flag (symbol: duration) so its description reads "Automatically
exit after N seconds (0 = run indefinitely)" and ensure the alias remains char:
"D" (i.e., restore the original wording while leaving required: false and other
options unchanged).
src/commands/apps/set-apns-p12.ts-57-59 (1)

57-59: ⚠️ Potential issue | 🟡 Minor

Progress message should end with ....

As per coding guidelines, progress messages must end with ... to indicate an in-progress action.

Proposed fix
      this.log(
-        progress(`Uploading APNS P12 certificate for app ${resource(args.id)}`),
+        progress(`Uploading APNS P12 certificate for app ${resource(args.id)}...`),
      );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/apps/set-apns-p12.ts` around lines 57 - 59, Update the progress
message passed to progress(...) so it ends with an ellipsis; specifically modify
the call in set-apns-p12 where this.log(progress(`Uploading APNS P12 certificate
for app ${resource(args.id)}`)) is used to append "..." to the string so the
progress output follows the guideline (i.e., ensure the template literal given
to progress includes trailing "...").
src/commands/channels/subscribe.ts-63-65 (1)

63-65: ⚠️ Potential issue | 🟡 Minor

Restore the standard --duration help text.

The description should include the indefinite-run behavior to match command help consistency.

Suggested patch
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/channels/subscribe.ts` around lines 63 - 65, Update the duration
flag's description in src/commands/channels/subscribe.ts: change the
Flags.integer({ description: ... , char: "D" }) entry so the description reads
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias
remains char: "D" to match the standard help text for the --duration/-D flag.
src/services/history-manager.ts-40-43 (1)

40-43: ⚠️ Potential issue | 🟡 Minor

Empty catch block violates ESLint no-empty rule.

The catch block at lines 40–43 contains only comments with no statements. ESLint 9's recommended rules enable no-empty by default; this will fail linting. Add a no-op statement to satisfy the rule while preserving the intended error-suppressing behavior.

Suggested fix
    } catch (error) {
      void error;
      // Silently ignore history load errors
      // History is a nice-to-have feature, shouldn't break the shell
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/history-manager.ts` around lines 40 - 43, The empty catch block
in history-manager.ts (the catch following the try that loads history, e.g., in
the loadHistory routine) violates ESLint no-empty; add a no-op statement inside
that catch (for example a void 0 expression statement) so the block is not empty
but preserves the silent error-suppressing behavior—do not change error handling
semantics or add throwing behavior.
src/commands/integrations/delete.ts-99-103 (1)

99-103: ⚠️ Potential issue | 🟡 Minor

Use dim styling for secondary field labels in the detail block.

Lines 99-103 currently print plain labels; they should use chalk.dim() to match the pattern used throughout the CLI for secondary labels in structured output.

Proposed fix
+import chalk from "chalk";
 import { Args, Flags } from "@oclif/core";
@@
-        this.log(`ID: ${integration.id}`);
-        this.log(`App ID: ${integration.appId}`);
-        this.log(`Type: ${integration.ruleType}`);
-        this.log(`Source Type: ${integration.source.type}`);
+        this.log(`${chalk.dim("ID:")} ${integration.id}`);
+        this.log(`${chalk.dim("App ID:")} ${integration.appId}`);
+        this.log(`${chalk.dim("Type:")} ${integration.ruleType}`);
+        this.log(`${chalk.dim("Source Type:")} ${integration.source.type}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/integrations/delete.ts` around lines 99 - 103, The printed
detail labels for integration fields (ID, App ID, Type, Source Type) are plain
text; update the output to use chalk.dim() for secondary labels to match CLI
styling—wrap the label portions passed to this.log (the strings like "ID:", "App
ID:", "Type:", "Source Type:") with chalk.dim() where the integration details
are printed (the block referencing integration.id, integration.appId,
integration.ruleType, integration.source.type), so each line becomes a dimmed
label followed by the value.
src/commands/channels/presence/subscribe.ts-38-42 (1)

38-42: ⚠️ Potential issue | 🟡 Minor

Duration flag description doesn't match coding guidelines.

The description was changed to "Automatically exit after N seconds" but the coding guidelines specify the exact wording should be "Automatically exit after N seconds (0 = run indefinitely)" with alias -D. The (0 = run indefinitely) part helps users understand that 0 or omitting the flag means indefinite execution.

🔧 Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/channels/presence/subscribe.ts` around lines 38 - 42, Update the
duration flag definition in subscribe.ts (the Flags.integer call for the
duration flag) to use the exact description "Automatically exit after N seconds
(0 = run indefinitely)" and ensure the short alias remains -D (char: "D") so the
flag matches the coding guideline.
src/commands/logs/channel-lifecycle/subscribe.ts-27-31 (1)

27-31: ⚠️ Potential issue | 🟡 Minor

Use the standardized --duration description text

Please update the description to include "(0 = run indefinitely)" for consistency with command guidelines.

Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines, "--duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/channel-lifecycle/subscribe.ts` around lines 27 - 31,
Update the duration flag definition used in subscribe.ts so its description
matches the standard text: change the Flags.integer({ description:
"Automatically exit after N seconds" ... }) to use "Automatically exit after N
seconds (0 = run indefinitely)" and keep the alias char: "D" and required: false
on the same Flags.integer call for the duration flag.
src/commands/channels/occupancy/subscribe.ts-36-40 (1)

36-40: ⚠️ Potential issue | 🟡 Minor

Align --duration description with the command guideline

duration uses -D correctly, but the description is missing the required "(0 = run indefinitely)" suffix.

Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines, "--duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/channels/occupancy/subscribe.ts` around lines 36 - 40, The
duration flag in src/commands/channels/occupancy/subscribe.ts uses Flags.integer
but its description is missing the required suffix; update the description for
the duration flag (the Flags.integer call named duration) to read "Automatically
exit after N seconds (0 = run indefinitely)" and keep the existing alias char:
"D".
src/commands/accounts/login.ts-230-230 (1)

230-230: ⚠️ Potential issue | 🟡 Minor

Avoid colored resource text inside progress() action text

progress() action text should stay uncolored; resource(appName) introduces color here.

Suggested fix
-              this.log(`\n${progress(`Creating app ${resource(appName)}`)}`);
+              this.log(`\n${progress(`Creating app ${appName}`)}`);

As per coding guidelines, "Use progress() helper for in-progress actions (no color on action text, ends with '...')."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/accounts/login.ts` at line 230, The progress action text
currently includes colored output via resource(appName) inside the progress(...)
call in the this.log(...) statement; replace the colored helper with plain
appName (or a stripped/colorless version) and ensure the progress text ends with
'...' so use progress(`Creating app ${appName}...`) (or progress(`Creating app
${stripColor(appName)}...`) if you need to strip color elsewhere) instead of
progress(`Creating app ${resource(appName)}`).
src/commands/bench/subscriber.ts-36-39 (1)

36-39: ⚠️ Potential issue | 🟡 Minor

Align --duration description with the standard text

Please include the (0 = run indefinitely) suffix in the help text for consistency.

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/bench/subscriber.ts` around lines 36 - 39, Update the duration
flag's description on the Flags.integer call for `duration` (the flag with char
"D") to match the standard text: change the description to "Automatically exit
after N seconds (0 = run indefinitely)". Ensure you only modify the description
string for the `duration` flag in the subscriber command where `duration:
Flags.integer({ char: "D", ... })` is defined.
src/commands/rooms/presence/enter.ts-46-48 (1)

46-48: ⚠️ Potential issue | 🟡 Minor

Use the standard --duration description text

Line 47 should include the indefinite-run hint for consistency across long-running commands.

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/presence/enter.ts` around lines 46 - 48, Update the
duration flag declaration in enter.ts so its description matches the standard
text and alias: change the Flags.integer description to "Automatically exit
after N seconds (0 = run indefinitely)" and ensure the char/alias is set to "D"
(refer to the duration flag definition in this file).
src/commands/logs/subscribe.ts-29-31 (1)

29-31: ⚠️ Potential issue | 🟡 Minor

Restore the standard --duration help text

Line 30 should include the indefinite-run note to match command help consistency.

Suggested fix
 duration: Flags.integer({
-  description: "Automatically exit after N seconds",
+  description: "Automatically exit after N seconds (0 = run indefinitely)",
   char: "D",
   required: false,
 }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/subscribe.ts` around lines 29 - 31, Update the
Flags.integer definition for the duration flag in subscribe.ts (the duration
flag declaration) to restore the standard help text: set its description to
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the char
alias remains "D" so the --duration flag matches the project guideline and help
output.
src/commands/rooms/typing/subscribe.ts-30-33 (1)

30-33: ⚠️ Potential issue | 🟡 Minor

Align --duration description with the shared command contract.

Line 31 should include the indefinite-run note for consistency across long-running commands.

Suggested fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/typing/subscribe.ts` around lines 30 - 33, The --duration
flag in the subscribe command currently has an incomplete description; update
the duration Flags.integer declaration (the duration flag in
src/commands/rooms/typing/subscribe.ts) so its description reads "Automatically
exit after N seconds (0 = run indefinitely)" and ensure the char alias is '-D'
(char: "D") to match the shared long-running command contract.
src/commands/logs/connection-lifecycle/subscribe.ts-22-24 (1)

22-24: ⚠️ Potential issue | 🟡 Minor

Use the standard --duration description text.

Line 23 should include the standard suffix for indefinite mode to match command consistency.

Suggested fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 22 - 24,
Update the duration flag description to the standard text and keep the alias; in
the Flags.integer call for the duration flag in subscribe.ts (the duration:
Flags.integer({...}) object), change description to "Automatically exit after N
seconds (0 = run indefinitely)" and ensure the char remains "D" so the flag
matches the project's standard --duration wording and alias.
src/commands/logs/push/subscribe.ts-27-31 (1)

27-31: ⚠️ Potential issue | 🟡 Minor

Use the standard --duration help text

Keep the duration description consistent with other commands and docs.

Suggested change
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/push/subscribe.ts` around lines 27 - 31, The duration flag
definition (duration: Flags.integer) uses a non-standard description; update its
description to "Automatically exit after N seconds (0 = run indefinitely)" and
ensure the alias/char remains "D" and required stays false so it matches other
commands and docs (change only the description string in the duration:
Flags.integer declaration).
src/commands/spaces/members/subscribe.ts-31-34 (1)

31-34: ⚠️ Potential issue | 🟡 Minor

Normalize --duration description text

Please use the shared duration wording used across long-running commands.

Suggested change
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/members/subscribe.ts` around lines 31 - 34, Update the
duration flag in the subscribe command so its description uses the shared
wording and alias: change the description string in the duration:
Flags.integer({ ... }) definition inside
src/commands/spaces/members/subscribe.ts to "Automatically exit after N seconds
(0 = run indefinitely)" and ensure the flag has char: "D" set (alias -D) and
required: false stays unchanged.
src/commands/rooms/messages/subscribe.ts-65-67 (1)

65-67: ⚠️ Potential issue | 🟡 Minor

Align --duration description with the CLI standard

The description should include the indefinite-run behavior note for consistency across long-running commands.

Suggested change
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/messages/subscribe.ts` around lines 65 - 67, Update the
duration flag metadata in the subscribe command: change the Flags.integer
description for the duration flag to "Automatically exit after N seconds (0 =
run indefinitely)" and ensure its alias remains char: "D" (the duration flag
definition using Flags.integer in subscribe.ts).
src/commands/spaces/cursors/subscribe.ts-36-38 (1)

36-38: ⚠️ Potential issue | 🟡 Minor

Use the standard help text for --duration.

Line 37 should include (0 = run indefinitely) for consistency across long-running commands.

Proposed fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines src/commands/**/*.ts: The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/subscribe.ts` around lines 36 - 38, The duration
flag's help text and alias need to follow the standard: update the Flags.integer
declaration for the duration flag (the symbol named duration in subscribe.ts) so
its description reads "Automatically exit after N seconds (0 = run
indefinitely)" and ensure the single-character alias is "D" (i.e., char: "D").
src/commands/spaces/locks/acquire.ts-36-39 (1)

36-39: ⚠️ Potential issue | 🟡 Minor

Standardize the --duration description text.

Line 37 should include the (0 = run indefinitely) suffix to match CLI conventions.

Proposed fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines src/commands/**/*.ts: The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locks/acquire.ts` around lines 36 - 39, Update the
duration flag's description in the Flags.integer call for the duration option so
it reads "Automatically exit after N seconds (0 = run indefinitely)"; ensure
this change is applied inside the duration: Flags.integer({ ... }) block and
retain the alias char: "D" so the flag remains available as -D.
.cursor/rules/AI-Assistance.mdc-135-135 (1)

135-135: ⚠️ Potential issue | 🟡 Minor

Keep the rule file consistent with the enforced --duration convention.

Line 135 currently contradicts the command guideline and will propagate inconsistent help text.

Proposed fix
-- `--duration`: `"Automatically exit after N seconds"`, alias `-D`
+- `--duration`: `"Automatically exit after N seconds (0 = run indefinitely)"`, alias `-D`

As per coding guidelines src/commands/**/*.ts: The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.cursor/rules/AI-Assistance.mdc at line 135, Update the `--duration` flag
entry in the AI-Assistance.mdc rule file so it matches the enforced convention:
change its description to 'Automatically exit after N seconds (0 = run
indefinitely)' and ensure the alias is `-D` for the `--duration` flag (look for
the `--duration` token on line containing the flag description to locate the
entry).
src/commands/rooms/typing/keystroke.ts-43-47 (1)

43-47: ⚠️ Potential issue | 🟡 Minor

Align --duration help text with the command convention.

Line 44 omits the (0 = run indefinitely) suffix used across commands.

Proposed fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines src/commands/**/*.ts: The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/typing/keystroke.ts` around lines 43 - 47, Update the
duration flag's description to match the project convention: change the
Flags.integer description for the duration flag (the property named duration
using Flags.integer) to "Automatically exit after N seconds (0 = run
indefinitely)"; keep the alias/char as "D" unchanged so the flag remains
available as -D.
src/commands/rooms/occupancy/subscribe.ts-45-47 (1)

45-47: ⚠️ Potential issue | 🟡 Minor

Use the standard --duration description string.

Line 46 should include the indefinite-run note for consistency with other long-running commands.

Proposed fix
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",

As per coding guidelines src/commands/**/*.ts: The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/occupancy/subscribe.ts` around lines 45 - 47, Update the
duration flag in src/commands/rooms/occupancy/subscribe.ts: modify the
Flags.integer configuration for the "duration" flag (symbol: duration, char:
"D") so its description exactly reads "Automatically exit after N seconds (0 =
run indefinitely)" to match the standard long-running command phrasing and
retain the alias -D.
src/commands/spaces/locations/subscribe.ts-55-57 (1)

55-57: ⚠️ Potential issue | 🟡 Minor

Restore the standard --duration help text

The current description drops the documented 0 semantics.

Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locations/subscribe.ts` around lines 55 - 57, Update the
duration flag definition in subscribe.ts (the duration: Flags.integer({...})
entry) to restore the standard help text: set description to "Automatically exit
after N seconds (0 = run indefinitely)" and ensure the alias char remains "D"
(i.e., preserve char: "D"); no other behavior changes required.
src/commands/rooms/messages/reactions/subscribe.ts-47-49 (1)

47-49: ⚠️ Potential issue | 🟡 Minor

Align --duration description with command standard

Please include the (0 = run indefinitely) behavior in the help text.

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/messages/reactions/subscribe.ts` around lines 47 - 49,
Update the --duration flag description to include the "(0 = run indefinitely)"
behavior; in the options definition for the duration flag (the object with
description: "Automatically exit after N seconds", char: "D", required: false in
subscribe.ts) replace the description string with "Automatically exit after N
seconds (0 = run indefinitely)" and ensure the alias char remains "D".
src/commands/spaces/locks/subscribe.ts-35-37 (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Use the standard --duration description text

The description should explicitly document the 0 behavior for indefinite run.

Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locks/subscribe.ts` around lines 35 - 37, Update the
duration flag declaration (duration: Flags.integer) to use the standard
description text "Automatically exit after N seconds (0 = run indefinitely)" and
ensure the alias char is '-D' (or char: "D") to match guidelines; locate the
duration flag in subscribe.ts (the duration: Flags.integer({...}) block) and
replace the current description with the exact required string.
src/commands/rooms/messages/history.ts-92-94 (1)

92-94: ⚠️ Potential issue | 🟡 Minor

Progress text is inaccurate for --order oldestFirst

The message always says “most recent messages,” which is misleading when users request oldest-first order.

Suggested wording
-              `Fetching ${flags.limit} most recent messages from room ${resource(args.room)}`,
+              `Fetching up to ${flags.limit} messages from room ${resource(args.room)}`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/messages/history.ts` around lines 92 - 94, Update the
progress message in the call to progress(...) so it reflects the requested
ordering: check the CLI flag used for ordering (e.g., flags.order or a boolean
like flags.oldestFirst) and change the text from "Fetching ${flags.limit} most
recent messages from room ${resource(args.room)}" to conditionally say "most
recent messages" when order is newest-first and "oldest messages" (or "oldest
messages first") when order is oldestFirst (i.e., when flags.order ===
'oldestFirst' or flags.oldestFirst is true); keep using flags.limit and
resource(args.room) and only alter the descriptive text.
🧹 Nitpick comments (7)
src/commands/support/ask.ts (1)

38-41: Use shared progress() output helper instead of raw ora

This command is still using ora(...) directly for in-progress output. Please switch to the shared progress() helper for consistent CLI output behavior across commands.

As per coding guidelines "Use progress() helper for in-progress actions (no color on action text, ends with '...')".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/support/ask.ts` around lines 38 - 41, Replace the direct ora
usage that sets spinner in the ask command with the shared progress() helper:
instead of creating spinner via ora("Thinking...").start(), call
progress("Thinking") when interactive output and JSON output are not enabled
(same condition using isInteractive || this.shouldOutputJson(flags)), and store
the returned progress handle in the same spinner variable so existing
stop/succeed calls still work; ensure the action text passed to progress has no
color and does not include the trailing ellipsis (progress will append "..."
itself).
test/unit/commands/stats/account.test.ts (1)

1-26: Use constructor-level Ably SDK stubbing for .test.ts

This file relies on network interception only. For unit-level isolation in this repo, please stub Ably.Rest constructor with sinon and restore in afterEach.

As per coding guidelines: "Mock the Ably SDK constructor (Ably.Rest) using sinon stubs, not just individual methods, and always restore stubs in afterEach".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/stats/account.test.ts` around lines 1 - 26, The test
currently relies only on nock; instead stub the Ably SDK constructor Ably.Rest
using sinon so the test is unit-scoped: import sinon and Ably, then in
beforeEach create a sinon.stub(Ably, "Rest").callsFake(() => ({ /* return the
minimal instance used by stats:account, e.g. stats: { get:
sinon.stub().resolves(mockStats) } */ })); use that stubbed instance to satisfy
the command run and keep the existing process.env setup; in afterEach restore
the stub by calling sinon.restore() (or (Ably.Rest as any).restore()) and keep
nock.cleanAll() so all stubs are removed between tests.
test/unit/commands/rooms/messages.test.ts (1)

437-440: Harden the relative-time assertion against runtime jitter.

This assertion can be flaky under slower CI timing. Consider validating against a captured before/after execution range instead of a single Date.now() sample.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/rooms/messages.test.ts` around lines 437 - 440, Replace
the single Date.now() sample check with a before/after range: capture const
before = Date.now() before you call the code that triggers
room.messages.history, and const after = Date.now() immediately after; then
assert callArgs.start is >= before - 5000 and <= after + 5000 (or tighter) so
the expectation around room.messages.history.mock.calls[0][0].start is tolerant
to CI jitter.
test/unit/commands/channels/history.test.ts (1)

191-195: Make relative-time assertions deterministic to reduce flaky CI runs.

These checks rely on wall-clock timing with a fixed ±5s window. On slow runners, that can fail intermittently. Prefer asserting within a before/after window captured around command execution (or use a fixed clock).

Also applies to: 207-211

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/channels/history.test.ts` around lines 191 - 195, The
relative-time assertions around channel.history.mock.calls[0][0].start are flaky
because they use Date.now() directly; instead capture a deterministic
before/after window around the command execution (e.g., const before =
Date.now(); run the code that triggers channel.history(...); const after =
Date.now();) and then assert callArgs.start is between before - tolerance and
after + tolerance (or use a mocked/fixed clock). Apply the same change for the
other similar assertions (the later block referencing channel.history.mock.calls
and its start value) so both tests use captured before/after timestamps (or a
fixed clock) rather than a single Date.now() comparison.
src/commands/spaces/locks/acquire.ts (1)

46-51: Release in finally should be gated by successful acquisition.

this.lockId is set from args before acquire, so Line 48 can attempt a release even when lock acquisition failed. Track acquisition state to avoid unnecessary release calls and noisy final-release errors.

Proposed fix
 export default class SpacesLocksAcquire extends SpacesBaseCommand {
   ...
   private lockId: null | string = null;
+  private lockAcquired = false;
 ...
   async finally(err: Error | undefined): Promise<void> {
-    if (this.lockId && this.space) {
+    if (this.lockAcquired && this.lockId && this.space) {
       try {
         await this.space.locks.release(this.lockId);
       } catch (error) {
         ...
       }
     }
 ...
         const lock = await this.space.locks.acquire(
           lockId,
           lockData as LockOptions,
         );
+        this.lockAcquired = true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locks/acquire.ts` around lines 46 - 51, The finally block
currently tries to release the lock whenever this.lockId is present, but
this.lockId is populated from args before acquire and can cause release attempts
when acquisition failed; introduce and set a boolean flag (e.g.,
this.lockAcquired or this.acquired) to true only after a successful call to the
acquire logic, then change the finally check to require both this.lockId and the
acquisition flag before calling this.space.locks.release(this.lockId) so release
is only attempted for locks that were actually acquired.
test/unit/commands/stats/app.test.ts (1)

33-36: Strengthen query assertions for time-range parsing.

query(true) only proves “some query exists”; it doesn’t verify that --start/--end were parsed and sent correctly. Please assert expected query keys/format so timestamp parsing regressions fail.

Suggested test hardening
-      .query(true)
+      .query((queryObject) => {
+        // ISO / unix-ms paths should produce numeric ms values
+        return typeof queryObject.start === "string" && /^\d+$/.test(queryObject.start);
+      })
-      .query(true)
+      .query((queryObject) => {
+        // ISO test should include end as well
+        return (
+          typeof queryObject.start === "string" &&
+          /^\d+$/.test(queryObject.start) &&
+          typeof queryObject.end === "string" &&
+          /^\d+$/.test(queryObject.end)
+        );
+      })

As per coding guidelines **/*.test.{ts,js}: Update or add Unit, Integration, and E2E tests as appropriate for your changes, following the testing strategy defined in docs/Testing.md.

Also applies to: 55-58, 70-73

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/stats/app.test.ts` around lines 33 - 36, Replace the loose
.query(true) nock matchers for the stats endpoint with specific assertions that
validate the start/end query params and their format: locate the nock setup that
creates scope from
nock("https://control.ably.net").get(`/v1/apps/${appId}/stats`) and change
.query(true) to either .query({ start: expectedStartIso, end: expectedEndIso })
when exact values are known, or .query(q => { return typeof q.start === 'string'
&& typeof q.end === 'string' && ISO_TIMESTAMP_REGEX.test(q.start) &&
ISO_TIMESTAMP_REGEX.test(q.end); }) to assert timestamp parsing; apply the same
change to the other occurrences of .query(true) for stats tests so regressions
in parsing --start/--end fail the tests.
src/commands/spaces/locks/subscribe.ts (1)

277-277: Style event action labels consistently

Consider highlighting updated as an event label using chalk.yellow() for consistency with other event outputs.

Suggested tweak
-            `${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)} updated`,
+            `${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)} ${chalk.yellow("updated")}`,

As per coding guidelines: "Use chalk.yellow() for event type labels in output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locks/subscribe.ts` at line 277, Change the event label
styling so the word "updated" is highlighted with chalk.yellow() for consistency
with other event outputs; locate the formatted message that uses
formatTimestamp(timestamp) and chalk.blue(lock.id) (in subscribe.ts) and replace
the plain "updated" label with chalk.yellow("updated") so the output reads
`${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)}
${chalk.yellow('updated')}` (ensure spacing/concatenation matches surrounding
messages).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a5a5c4e8-87a8-47a6-81d0-edb6648fdcb8

📥 Commits

Reviewing files that changed from the base of the PR and between adedf69 and 0794c34.

📒 Files selected for processing (80)
  • .claude/CLAUDE.md
  • .cursor/rules/AI-Assistance.mdc
  • src/commands/accounts/login.ts
  • src/commands/apps/channel-rules/create.ts
  • src/commands/apps/channel-rules/delete.ts
  • src/commands/apps/channel-rules/list.ts
  • src/commands/apps/create.ts
  • src/commands/apps/delete.ts
  • src/commands/apps/set-apns-p12.ts
  • src/commands/apps/update.ts
  • src/commands/auth/keys/create.ts
  • src/commands/bench/subscriber.ts
  • src/commands/channels/batch-publish.ts
  • src/commands/channels/history.ts
  • src/commands/channels/list.ts
  • src/commands/channels/occupancy.ts
  • src/commands/channels/occupancy/get.ts
  • src/commands/channels/occupancy/subscribe.ts
  • src/commands/channels/presence/enter.ts
  • src/commands/channels/presence/subscribe.ts
  • src/commands/channels/publish.ts
  • src/commands/channels/subscribe.ts
  • src/commands/integrations/delete.ts
  • src/commands/interactive.ts
  • src/commands/logs/channel-lifecycle/subscribe.ts
  • src/commands/logs/connection-lifecycle/history.ts
  • src/commands/logs/connection-lifecycle/subscribe.ts
  • src/commands/logs/history.ts
  • src/commands/logs/push/history.ts
  • src/commands/logs/push/subscribe.ts
  • src/commands/logs/subscribe.ts
  • src/commands/queues/delete.ts
  • src/commands/rooms/list.ts
  • src/commands/rooms/messages/history.ts
  • src/commands/rooms/messages/reactions/remove.ts
  • src/commands/rooms/messages/reactions/send.ts
  • src/commands/rooms/messages/reactions/subscribe.ts
  • src/commands/rooms/messages/send.ts
  • src/commands/rooms/messages/subscribe.ts
  • src/commands/rooms/occupancy/get.ts
  • src/commands/rooms/occupancy/subscribe.ts
  • src/commands/rooms/presence/enter.ts
  • src/commands/rooms/presence/subscribe.ts
  • src/commands/rooms/reactions/send.ts
  • src/commands/rooms/reactions/subscribe.ts
  • src/commands/rooms/typing/keystroke.ts
  • src/commands/rooms/typing/subscribe.ts
  • src/commands/spaces/cursors/get-all.ts
  • src/commands/spaces/cursors/set.ts
  • src/commands/spaces/cursors/subscribe.ts
  • src/commands/spaces/list.ts
  • src/commands/spaces/locations.ts
  • src/commands/spaces/locations/get-all.ts
  • src/commands/spaces/locations/set.ts
  • src/commands/spaces/locations/subscribe.ts
  • src/commands/spaces/locks/acquire.ts
  • src/commands/spaces/locks/get-all.ts
  • src/commands/spaces/locks/get.ts
  • src/commands/spaces/locks/subscribe.ts
  • src/commands/spaces/members/enter.ts
  • src/commands/spaces/members/subscribe.ts
  • src/commands/stats/account.ts
  • src/commands/stats/app.ts
  • src/commands/support/ask.ts
  • src/flags.ts
  • src/services/history-manager.ts
  • src/utils/time.ts
  • test/e2e/interactive/ctrl-c-behavior.test.ts
  • test/e2e/spaces/spaces-e2e.test.ts
  • test/unit/commands/apps/set-apns-p12.test.ts
  • test/unit/commands/channels/batch-publish.test.ts
  • test/unit/commands/channels/history.test.ts
  • test/unit/commands/did-you-mean.test.ts
  • test/unit/commands/interactive-sigint.test.ts
  • test/unit/commands/interactive.test.ts
  • test/unit/commands/queues/delete.test.ts
  • test/unit/commands/rooms/messages.test.ts
  • test/unit/commands/stats/account.test.ts
  • test/unit/commands/stats/app.test.ts
  • test/unit/utils/time.test.ts
💤 Files with no reviewable changes (3)
  • test/e2e/interactive/ctrl-c-behavior.test.ts
  • test/e2e/spaces/spaces-e2e.test.ts
  • src/commands/interactive.ts

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 81 out of 81 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 81 out of 81 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

src/utils/time.ts:1

  • The docstring says “ISO 8601 or any Date-parseable string”, but the implementation now rejects non-ISO strings via the isIso regex guard. Update the comment to match actual behavior (e.g., “ISO 8601 only”) or relax the validation to allow general Date-parseable inputs.
    src/utils/time.ts:1
  • The regex permits date-times with no timezone (the (Z|[+-]...)? is optional), which new Date() interprets in local time. That makes parseTimestamp("2023-01-01T00:00") machine/timezone-dependent and can lead to inconsistent CLI behavior. Consider requiring an explicit timezone on date-time inputs (keep date-only as-is), or normalize timezone-less inputs to UTC explicitly.
    src/commands/queues/delete.ts:1
  • In --json mode with no --force, the command will skip confirmation and proceed with deletion. To avoid accidental destructive actions in non-interactive/scripted usage, consider requiring --force whenever confirmation cannot be shown (e.g., --json or non-TTY), and emit a structured JSON error telling the user to pass --force.
    src/commands/spaces/cursors/get-all.ts:1
  • This changes cursor updates from an in-place terminal update (process.stdout.write("\r...")) to line-by-line logging. For long-running subscriptions this can generate a lot of output (and slow down / bloat logs). If shouldUseTerminalUpdates() still exists, consider restoring the in-place update for TTY (non-JSON) and falling back to this.log() only for non-TTY or when explicitly requested.
    test/unit/commands/stats/app.test.ts:1
  • These tests validate that the command runs, but .query(true) doesn’t assert that --start/--end are actually parsed and sent correctly (especially important for relative timestamps). Consider freezing time (fake timers) and asserting the exact expected query params (or at least that start/end exist and are numeric ms) so regressions in parseTimestamp() wiring are caught.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sacOO7
Copy link
Contributor

sacOO7 commented Mar 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 81 out of 81 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Resolve merge conflicts in channel-rules create/delete (redundant appId assignment)
- Fix history commands passing "Unknown timestamp" through formatTimestamp()
  - channels/history, logs/history, logs/connection-lifecycle/history, logs/push/history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
src/commands/stats/app.ts (2)

177-184: ⚠️ Potential issue | 🟠 Major

Keep the shutdown message out of JSON stdout.

Line 183 still prints plain text during --live --json, so terminating the command breaks machine-readable output even though the new startup path is JSON-aware. Guard this log with shouldOutputJson() or send it to stderr instead.

Suggested fix
       const cleanup = () => {
         if (this.pollInterval) {
           clearInterval(this.pollInterval);
           this.pollInterval = undefined;
         }
 
-        this.log("\nUnsubscribed from live stats");
+        if (!this.shouldOutputJson(flags)) {
+          this.log("\nUnsubscribed from live stats");
+        }
       };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/stats/app.ts` around lines 177 - 184, The cleanup() function
prints a plain-text shutdown message via this.log which corrupts JSON-only
output; change it to either call this.log only when shouldOutputJson() is false
or send the message to stderr (e.g., use this.logError or process.stderr.write)
so that when shouldOutputJson() is true no plain text is written. Update the
block that clears this.pollInterval and the final this.log("\nUnsubscribed from
live stats") to be conditional on shouldOutputJson() (or routed to stderr) to
keep JSON stdout machine-readable.

37-44: ⚠️ Potential issue | 🟠 Major

--live accepts --start/--end and then ignores them.

After adding ...timeRangeFlags, this command will happily parse --start / --end in live mode, but fetchAndDisplayStats() always requests the last 24 hours. That makes the CLI silently disregard user input. Either reject those flags with --live or thread them through the live fetch path.

Suggested fix
     if (flags.live && flags.unit !== "minute") {
       this.warn(
         "Live stats only support minute intervals. Using minute interval.",
       );
       flags.unit = "minute";
     }
+
+    if (flags.live && (flags.start || flags.end)) {
+      this.error("--start/--end are not supported with --live");
+    }

Also applies to: 164-190

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/stats/app.ts` around lines 37 - 44, The live mode currently
accepts --start/--end from timeRangeFlags but fetchAndDisplayStats() ignores
them and always uses the last 24 hours; update the command so live mode either
rejects start/end or respects them: inside the command's run() (or the
flag-parsing path that handles the live boolean) add validation that if
flags.live is true and either flags.start or flags.end is provided you either
throw a usage error (fail fast) or pass those parsed start/end values into the
live polling call, making fetchAndDisplayStats() (or the live fetch helper it
calls) accept optional start/end parameters and use them instead of the default
last-24-hours range when provided; reference the static flags (timeRangeFlags),
the flags.live boolean, and fetchAndDisplayStats() to locate where to add
validation or parameter threading.
src/commands/stats/account.ts (2)

159-166: ⚠️ Potential issue | 🟡 Minor

Cleanup message may pollute JSON output.

The cleanup handler logs "Unsubscribed from live stats" unconditionally (line 165), which could interfere with JSON parsing when --json is used. Consider guarding this message.

Proposed fix
       const cleanup = () => {
         if (this.pollInterval) {
           clearInterval(this.pollInterval);
           this.pollInterval = undefined;
         }
-
-        this.log("\nUnsubscribed from live stats");
+        if (!this.shouldOutputJson(flags)) {
+          this.log("\nUnsubscribed from live stats");
+        }
       };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/stats/account.ts` around lines 159 - 166, The cleanup function
unconditionally calls this.log("\nUnsubscribed from live stats"), which can
corrupt machine-readable output; modify cleanup in the same block (the cleanup
arrow function that clears this.pollInterval) to only emit that human-readable
message when not running in JSON mode (check the command's JSON flag/property,
e.g. this.json or a --json option) — otherwise suppress the log so only valid
JSON remains.

197-207: ⚠️ Potential issue | 🟡 Minor

Unreachable cleanup code after this.error().

this.error() throws an exception in oclif, so lines 204-206 are unreachable when not in JSON mode. Consider restructuring to ensure cleanup runs before the error:

Proposed fix
     } catch (error) {
+      if (this.pollInterval) {
+        clearInterval(this.pollInterval);
+      }
       const errorMsg = `Error setting up live stats: ${error instanceof Error ? error.message : String(error)}`;
       if (this.shouldOutputJson(flags)) {
         this.jsonError({ error: errorMsg, success: false }, flags);
       } else {
         this.error(errorMsg);
       }
-      if (this.pollInterval) {
-        clearInterval(this.pollInterval);
-      }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/stats/account.ts` around lines 197 - 207, The catch block calls
this.error() which throws, making clearInterval(this.pollInterval) unreachable;
move the cleanup before throwing or ensure cleanup always runs by clearing the
interval at the top of the catch block (i.e., call if (this.pollInterval)
clearInterval(this.pollInterval); before invoking this.jsonError or this.error
in the catch inside the method where the try/catch currently lives), or
alternatively capture the error, perform cleanup, then rethrow or call
this.error; reference this.error, this.jsonError, and this.pollInterval when
applying the change.
src/commands/logs/history.ts (1)

103-109: ⚠️ Potential issue | 🟡 Minor

Potential issue: formatTimestamp may receive a non-timestamp string.

When message.timestamp is falsy, timestamp is set to "Unknown timestamp" (line 105), but this string is then passed to formatTimestamp (line 108). Depending on how formatTimestamp handles non-date strings, this could produce unexpected output or errors.

Consider guarding the call:

Proposed fix
-          this.log(
-            `${chalk.dim(`[${index + 1}]`)} ${formatTimestamp(timestamp)}`,
-          );
+          const formattedTime = timestamp === "Unknown timestamp"
+            ? chalk.dim("[Unknown timestamp]")
+            : formatTimestamp(timestamp);
+          this.log(
+            `${chalk.dim(`[${index + 1}]`)} ${formattedTime}`,
+          );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/history.ts` around lines 103 - 109, The code sets timestamp
to "Unknown timestamp" when message.timestamp is falsy but still passes that
string into formatTimestamp; update the logic around the timestamp handling in
the history command so formatTimestamp is only called with a real Date/ISO
string (or a nullable value) — e.g., check message.timestamp (or the local
timestamp variable) before invoking formatTimestamp and render the literal
"Unknown timestamp" directly when absent; locate the timestamp assignment and
the this.log call in the function handling message rendering (references:
message.timestamp, timestamp variable, formatTimestamp) and implement the guard
or conditional formatting there.
src/commands/spaces/cursors/set.ts (3)

49-72: ⚠️ Potential issue | 🟠 Major

--duration 0 is inverted from the shared long-running command contract.

The flag surface now uses the shared -D alias, but this command still treats flags.duration === 0 as “exit almost immediately” at Lines 394-401. Other long-running commands use 0 = run indefinitely, so users will get the opposite behavior here unless the implementation and help text are aligned.

As per coding guidelines, "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/set.ts` around lines 49 - 72, Update the duration
flag help text and runtime handling so 0 means run indefinitely (consistent with
other long-running commands): change the Flags.integer description for duration
to "Automatically exit after N seconds (0 = run indefinitely)" (it already has
char: "D"), and modify the logic that currently treats flags.duration === 0 as
"exit immediately" (the check around flags.duration in this command's run loop /
the block referenced at lines ~394-401) to instead treat 0 as no timeout (i.e.,
skip automatic exit when duration === 0) so behavior and help text are aligned.

110-153: ⚠️ Potential issue | 🟠 Major

Finish the JSON error path for input validation.

These new jsonError() branches only cover JSON.parse() failures. If the JSON parses but position is missing/typed wrong at Line 164, or no position input is supplied at Line 170, the command still falls back to this.error(). That leaves --json callers with mixed output formats for the same class of validation errors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/set.ts` around lines 110 - 153, After parsing
flags.data into cursorData (and after building cursorData from flags.x/flags.y
plus parsed additionalData), validate that cursorData.position exists and that
position.x and position.y are numbers; if validation fails or no position was
supplied in the flags.data-only path, return the same JSON-formatted error when
shouldOutputJson(flags) is true by calling this.jsonError({ error: <message>,
success: false }, flags) and otherwise call this.error(<message>); update the
code paths around the JSON.parse success in the handlers that set cursorData
(referencing jsonError, shouldOutputJson, cursorData, flags, and this.error) to
produce consistent JSON output for missing or incorrectly typed position data.

105-116: ⚠️ Potential issue | 🟡 Minor

Reject non-object sidecar metadata before assigning cursorData.data.

JSON.parse() accepts any JSON value here, but this command documents --data as key/value cursor metadata. Inputs like --data 'false' or --data '0' are currently accepted and then dropped by the truthiness check at Line 259; arrays and strings are forwarded with the wrong shape. Please require a plain object before storing it under data.

Also applies to: 127-138

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/set.ts` around lines 105 - 116, The parsed --data
value must be validated as a plain object before assigning to cursorData.data
(and the other similar assignment block that sets cursorUpdate.data); after
JSON.parse(flags.data) check that the result is !== null, typeof result ===
'object' and !Array.isArray(result) and only then set cursorData.data (or
cursorUpdate.data); if the check fails, reuse the existing error handling path
(this.shouldOutputJson => this.jsonError(...) else this.error(...)) with the
same "Invalid JSON in --data flag..." message so primitives, arrays, nulls and
other non-object JSON are rejected.
🧹 Nitpick comments (10)
src/commands/logs/connection-lifecycle/subscribe.ts (2)

22-26: Duration flag description is incomplete per coding guidelines.

The description should include the (0 = run indefinitely) clarification.

📝 Suggested fix
     duration: Flags.integer({
-      description: "Automatically exit after N seconds",
+      description: "Automatically exit after N seconds (0 = run indefinitely)",
       char: "D",
       required: false,
     }),

As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 22 - 26,
The duration flag definition (duration: Flags.integer) has an incomplete
description; update its description to "Automatically exit after N seconds (0 =
run indefinitely)" and ensure the alias char remains "D" (alias -D) in the
Flags.integer call so the flag matches coding guidelines; locate the duration
flag in subscribe.ts and replace the current description string accordingly.

106-110: Secondary label should use chalk.dim() instead of chalk.green().

Per the PR objectives and coding guidelines, secondary labels like "Data:" should use chalk.dim() rather than chalk.green().

📝 Suggested fix
           if (message.data !== null && message.data !== undefined) {
             this.log(
-              `${chalk.green("Data:")} ${JSON.stringify(message.data, null, 2)}`,
+              `${chalk.dim("Data:")} ${JSON.stringify(message.data, null, 2)}`,
             );
           }

As per coding guidelines: "Use chalk.dim() for secondary labels like field names in structured output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 106 - 110,
The secondary label "Data:" in the message handling of the subscribe command is
using chalk.green(); change it to chalk.dim() so secondary labels follow the
project's style guide—locate the block that checks message.data !== null &&
message.data !== undefined inside the subscribe handler and replace
chalk.green("Data:") with chalk.dim("Data:") in the this.log call that
JSON.stringifies message.data.
test/unit/commands/queues/delete.test.ts (1)

421-423: Consider updating skipped test expectations for consistency.

These skipped tests still expect the old message format (Queue "${mockQueueName}" and "deleted successfully"), while the active tests now expect "Queue deleted:". If these tests are ever unskipped, they would fail due to the format mismatch.

💡 Suggested update
-      expect(stdout).toContain(`Queue "${mockQueueName}"`);
-      expect(stdout).toContain("deleted successfully");
+      expect(stdout).toContain("Queue deleted:");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/queues/delete.test.ts` around lines 421 - 423, The skipped
assertions in delete.test.ts still expect the old output format ("Queue
\"${mockQueueName}\"" and "deleted successfully") which now conflicts with
active tests that assert "Queue deleted:"; update the skipped test expectations
to match the new format by changing the expect calls that reference stdout and
mockQueueName to assert the "Queue deleted:" style (use the same stdout contains
checks as the active tests) so that if the tests are unskipped they will pass
consistently with the current output formatting.
src/commands/apps/set-apns-p12.ts (1)

57-59: Progress message should end with ...

Per coding guidelines, progress messages should end with ... to indicate an in-progress action.

Suggested fix
     this.log(
-      progress(`Uploading APNS P12 certificate for app ${resource(args.id)}`),
+      progress(`Uploading APNS P12 certificate for app ${resource(args.id)}...`),
     );

As per coding guidelines: "Use progress() helper for in-progress actions (no color on action text, ends with '...')"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/apps/set-apns-p12.ts` around lines 57 - 59, The progress message
logged by this.log(progress(...)) does not end with an ellipsis; update the
string passed to progress in the set-apns-p12 command so it ends with "..."
(e.g. change `Uploading APNS P12 certificate for app ${resource(args.id)}` to
`Uploading APNS P12 certificate for app ${resource(args.id)}...`). Locate the
call to progress(...) inside the command (the this.log(progress(...))
invocation) and append the three dots to the template literal so the progress
helper output follows the coding guideline.
.claude/CLAUDE.md (1)

127-127: Consider expanding the relative time format examples.

The documentation shows examples "1h", "30m", "2d", but the PR summary indicates additional supported formats including "30s", "5m", and "1w". Including a more comprehensive set of examples would help developers understand the full range of supported formats.

📝 Suggested enhancement
-- **`timeRangeFlags`** — `--start`, `--end`. Use for history and stats commands. Parse with `parseTimestamp()` from `src/utils/time.ts`. Accepts ISO 8601, Unix ms, or relative (e.g., `"1h"`, `"30m"`, `"2d"`).
+- **`timeRangeFlags`** — `--start`, `--end`. Use for history and stats commands. Parse with `parseTimestamp()` from `src/utils/time.ts`. Accepts ISO 8601, Unix ms, or relative (e.g., `"30s"`, `"5m"`, `"1h"`, `"2d"`, `"1w"`).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/CLAUDE.md at line 127, Update the documentation for timeRangeFlags
to show a more comprehensive set of relative time examples supported by
parseTimestamp() in src/utils/time.ts (e.g., "30s", "1m", "5m", "30m", "1h",
"1d", "2d", "1w") so readers clearly see seconds/minutes/hours/days/weeks are
accepted; edit the line describing **`timeRangeFlags`** (the `--start`, `--end`
examples) to include these additional relative formats.
src/commands/integrations/delete.ts (1)

92-116: Consider using chalk.dim() for secondary field labels.

The JSON output structure looks good. For the non-JSON output, the coding guidelines recommend using chalk.dim() for secondary labels like field names in structured output. The labels on lines 112-115 (ID:, App ID:, etc.) could use this styling for consistency with other commands.

♻️ Optional: Apply chalk.dim() to field labels

Add chalk import:

+import chalk from "chalk";
 import { Args, Flags } from "@oclif/core";

Then update the output:

       } else {
         this.log(
           success(`Integration rule deleted: ${resource(integration.id)}.`),
         );
-        this.log(`ID: ${integration.id}`);
-        this.log(`App ID: ${integration.appId}`);
-        this.log(`Type: ${integration.ruleType}`);
-        this.log(`Source Type: ${integration.source.type}`);
+        this.log(`${chalk.dim("ID:")} ${integration.id}`);
+        this.log(`${chalk.dim("App ID:")} ${integration.appId}`);
+        this.log(`${chalk.dim("Type:")} ${integration.ruleType}`);
+        this.log(`${chalk.dim("Source Type:")} ${integration.source.type}`);
       }

As per coding guidelines: "Use chalk.dim() for secondary labels like field names in structured output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/integrations/delete.ts` around lines 92 - 116, The non-JSON
output in the delete command prints field labels as plain text—wrap the
secondary labels (the static parts in the this.log calls that print `ID:`, `App
ID:`, `Type:`, `Source Type:`) with chalk.dim() for consistent styling; add an
import for chalk at the top of the file and update the four this.log(...) lines
in the else branch (the block guarded by this.shouldOutputJson(flags) and using
success(), resource(), and formatJsonOutput) so each static label is formatted
via chalk.dim() while keeping the dynamic values unchanged.
src/commands/support/ask.ts (1)

38-43: Use the shared progress() helper here.

This branch re-implements the in-progress UI with ora plus a manual this.log("Thinking..."), so support ask now diverges from the shared command UX the rest of this PR is standardizing. Routing this through progress("Thinking...") would keep the behavior consistent in one place.

As per coding guidelines, "Use progress() helper for in-progress actions (no color on action text, ends with '...')".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/support/ask.ts` around lines 38 - 43, Replace the ad-hoc ora
spinner and manual this.log("Thinking...") with the shared progress helper:
remove the ora creation and the conditional this.log call in the support ask
command and instead call progress("Thinking...") where the current spinner logic
runs (respecting the same isInteractive / shouldOutputJson(flags) condition);
ensure you import or reference the progress() helper and use its returned
handle/stop behavior consistent with other commands so the in-progress UI
matches the shared UX.
test/unit/utils/time.test.ts (1)

83-88: Always restore fake timers in this test.

If the assertion on Line 87 fails, Line 88 never runs and the suite keeps fake timers enabled. Wrap this setup in try/finally or move it into a nested beforeEach/afterEach block for the whitespace suite.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/utils/time.test.ts` around lines 83 - 88, The test "should trim
whitespace from relative input" uses vi.useFakeTimers() but may leave fake
timers enabled if the assertion throws; wrap the fake-timer setup and assertion
in a try/finally (or move to a nested beforeEach/afterEach) so
vi.useRealTimers() always runs. Specifically, in the test containing
parseTimestamp(" 1h "), ensure vi.useRealTimers() is called in a finally block
(or ensure the whitespace suite uses beforeEach to call vi.useFakeTimers() and
afterEach to call vi.useRealTimers()) so timers are always restored even on
assertion failure.
src/commands/rooms/messages/history.ts (1)

67-69: Unreachable code after this.error().

In oclif, this.error() throws an exception and never returns, making the return statement on line 68 unreachable. While harmless, it can be removed for clarity.

Proposed fix
       if (!chatClient) {
         this.error("Failed to create Chat client");
-        return;
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/rooms/messages/history.ts` around lines 67 - 69, The code calls
this.error("Failed to create Chat client") which in oclif throws and never
returns, so remove the redundant unreachable return statement immediately after
it (in the same block where the Chat client is created), leaving only the
this.error(...) call; locate the occurrence in the history command handler (the
method containing the Chat client creation) and delete the trailing return to
clean up unreachable code.
src/commands/logs/push/history.ts (1)

2-2: Make ably a type-only import

Ably is only used for a type annotation here. Switching this to import type avoids a needless runtime import under type-preserving TS emits and usually keeps consistent-type-imports-style lint rules happy.

♻️ Proposed fix
-import * as Ably from "ably";
+import type * as Ably from "ably";

As per coding guidelines, "Ensure all code passes ESLint linter checks using pnpm exec eslint."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/logs/push/history.ts` at line 2, Replace the runtime import of
Ably with a type-only import since the symbol Ably is used only for type
annotations; change the import statement that currently reads `import * as Ably
from "ably";` to a type-only import (e.g., using `import type`) so the module is
not included at runtime and ESLint consistent-type-imports rules are satisfied,
then run the linter to verify (refer to the Ably symbol in this file when making
the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/channels/history.ts`:
- Around line 80-87: Reject inverted time ranges by validating that when both
flags.start and flags.end are present (after converting with parseTimestamp),
the resulting values produce start <= end; if start > end throw or return a
clear error before calling channel.history(). Update the block that sets
historyParams (using parseTimestamp and historyParams.start/historyParams.end)
to perform this check and surface a user-friendly message referencing the flags
(e.g., "start must be earlier than or equal to end") instead of proceeding to
channel.history().

In `@src/commands/channels/presence/enter.ts`:
- Around line 43-47: Update the duration flag's description on the Flags.integer
configuration for the duration option so it reads "Automatically exit after N
seconds (0 = run indefinitely)"; locate the duration flag definition (the
duration: Flags.integer({...}) object) in enter.ts and change only the
description string to append " (0 = run indefinitely)".

In `@src/commands/logs/channel-lifecycle/subscribe.ts`:
- Around line 27-31: Update the duration flag definition in subscribe.ts (the
duration: Flags.integer({...}) entry) to use the repo-standard help text
exactly: set description to "Automatically exit after N seconds (0 = run
indefinitely)" and keep the alias char "D" so the flag reads and behaves
consistently with other long-running commands.

In `@src/commands/logs/push/history.ts`:
- Around line 57-64: Parse the relative start/end inputs before awaiting
createAblyRestClient so the timestamps reflect the CLI invocation time;
specifically call parseTimestamp on flags.start and flags.end prior to the await
this.createAblyRestClient(flags), store the parsed values (e.g., parsedStart,
parsedEnd), and then assign those to historyOptions.start/historyOptions.end
when building historyOptions later (instead of calling parseTimestamp after the
await). Ensure you keep using the same parsed variables and still validate
presence of flags.start/flags.end.

In `@src/commands/logs/push/subscribe.ts`:
- Around line 27-31: Update the duration flag definition in subscribe.ts (the
Flags.integer call for the "duration" flag) to use the standard description
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias
char remains "D" so it matches the rest of the CLI surface and guidelines.

In `@src/commands/rooms/occupancy/get.ts`:
- Line 4: The new import of productApiFlags removed the client-id flag; restore
preservation of the client identity by adding the existing clientIdFlag back
into this command's flag surface alongside productApiFlags (i.e., merge
clientIdFlag with productApiFlags in the command's flags definition in get.ts)
so that --client-id remains supported for Chat realtime room attachment flows;
reference the clientIdFlag symbol and productApiFlags symbol when making the
change.

In `@src/commands/rooms/presence/enter.ts`:
- Around line 46-49: Restore the standard help text for the duration flag:
update the Flags.integer definition for the duration flag (the `duration`
symbol) so its description string is "Automatically exit after N seconds (0 =
run indefinitely)" and ensure the alias remains char: "D" (i.e., keep -D). This
ensures the CLI help accurately documents the 0 = run indefinitely behavior for
the duration flag.

In `@src/commands/rooms/presence/subscribe.ts`:
- Around line 44-46: The duration flag description was shortened; update the
Flags.integer call for the duration flag (symbol: duration) to use the standard
text "Automatically exit after N seconds (0 = run indefinitely)" and ensure the
char alias is 'D' (alias -D) so the CLI help matches shared wording and
documents indefinite-run behavior.

In `@src/commands/rooms/reactions/subscribe.ts`:
- Line 6: The command replaced an explicit client-id flag by importing
productApiFlags, removing the --client-id option; restore the clientIdFlag into
the command's flags so realtime commands (like the subscribe command that
imports productApiFlags) accept --client-id again by merging or adding
clientIdFlag alongside productApiFlags (ensure the flags object used by the
command includes both productApiFlags and clientIdFlag symbols so the realtime
connection can receive an explicit client identity).
- Around line 34-36: The duration flag's help text was changed and no longer
mentions that 0 runs indefinitely; update the description for the duration
Flags.integer declaration (the duration flag configured with Flags.integer and
char: "D") to read exactly "Automatically exit after N seconds (0 = run
indefinitely)" and ensure the alias remains -D so --help shows the standard
behavior.

In `@src/commands/rooms/typing/keystroke.ts`:
- Around line 43-47: Update the duration flag definition for the `duration`
Flags.integer entry in keystroke.ts to match the shared long-running flag
contract: keep the alias char 'D' (short flag -D) and change the description to
"Automatically exit after N seconds (0 = run indefinitely)". Locate the
`duration` Flags.integer block and replace the existing description string with
the standardized one so this command is consistent with other long-running
commands.
- Line 38: Add the client identity flag to this typing keystroke command by
including ...clientIdFlag alongside ...productApiFlags in the command flags list
(so locate where productApiFlags is spread and add ...clientIdFlag); also update
the duration flag's description (the duration flag constant or flags entry named
duration) to read "Automatically exit after N seconds (0 = run indefinitely)".
Ensure you only modify the flags object for the keystroke/send command
(referencing productApiFlags, clientIdFlag, and duration).

In `@src/commands/spaces/locations/get-all.ts`:
- Around line 162-181: The extractLocationData helper is incorrectly treating
current/member fields as data, causing locations with the current.member shape
to include wrapper metadata and lose memberId; update knownMetaKeys to include
"current" and "member" (and possibly "memberId" already present) and adjust the
JSON-branch logic in extractLocationData (and the equivalent blocks at the other
occurrences) to explicitly check for member.memberId or current.member and, when
present, prefer returning that nested member/location value or null instead of
the whole object so memberId extraction later does not fall back to "Unknown".

In `@src/commands/spaces/members/enter.ts`:
- Around line 40-44: The duration flag definition uses a nonstandard
description; update the Flags.integer call for the duration flag (the object
with key duration, char "D") to use the repository-standard help text exactly:
"Automatically exit after N seconds (0 = run indefinitely)"; keep the alias char
"D" and required: false unchanged so the flag matches other long-running
commands.

In `@src/commands/spaces/members/subscribe.ts`:
- Around line 31-35: The --duration flag description in the duration Flag
definition inside src/commands/spaces/members/subscribe.ts should match the
shared wording; update the description for the duration Flag (the object
assigned to duration in the Flags.integer call in the subscribe command) to
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the flag
char is "D" (alias -D) to maintain consistency with other long-running commands.

In `@src/commands/stats/account.ts`:
- Around line 224-247: The code currently leaves startMs or endMs undefined when
only one of --start or --end is provided; update the logic after parseTimestamp
so that if only startMs is set, set endMs = startMs + 24*60*60*1000, and if only
endMs is set, set startMs = endMs - 24*60*60*1000, before calling
controlApi.getAccountStats; reference variables/functions: startMs, endMs,
parseTimestamp, flags.start, flags.end, controlApi.getAccountStats.

In `@src/commands/support/ask.ts`:
- Around line 90-111: The fenced code blocks in response.answer are being
post-processed by inline markdown replacements (the current
processedWithCodeBlocks approach), causing backticks/asterisks inside fences to
be mangled; fix it by extracting fenced blocks first into an array (e.g., create
codeBlocks: string[]), replace each fence in response.answer with a unique
placeholder (use a regex like /```[^\n]*\n([\S\s]*?)```/g), store the
highlighted/chalked code block strings in codeBlocks, then run the existing
.replaceAll(...) chain on the placeholder-containing string (use
withoutCodeBlocks instead of processedWithCodeBlocks) and finally restore blocks
by replacing placeholders (e.g., /__CODE_BLOCK_(\d+)__/g) with
codeBlocks[Number(index)] when building formattedAnswer.

In `@test/unit/commands/channels/batch-publish.test.ts`:
- Around line 261-268: The current assertions check fragments separately,
allowing labels to be misassociated; update the test to assert each rendered
item as a whole by matching related fragments together (e.g., for the success
case ensure a single line contains "Published to channel", "channel1", and
"msg-1", and for the failure case ensure a single line contains "Failed to
publish to channel", "channel2", and the error "Invalid channel name (40000)").
Locate the assertions that reference stdout, channel1, channel2, msg-1 and
Invalid channel name (40000) and replace the loose expect(...toContain...) calls
with either a single regex match against stdout (e.g., /Published to
channel.*channel1.*msg-1/) or split stdout into lines and assert a specific line
includes all expected substrings for each item.

---

Outside diff comments:
In `@src/commands/logs/history.ts`:
- Around line 103-109: The code sets timestamp to "Unknown timestamp" when
message.timestamp is falsy but still passes that string into formatTimestamp;
update the logic around the timestamp handling in the history command so
formatTimestamp is only called with a real Date/ISO string (or a nullable value)
— e.g., check message.timestamp (or the local timestamp variable) before
invoking formatTimestamp and render the literal "Unknown timestamp" directly
when absent; locate the timestamp assignment and the this.log call in the
function handling message rendering (references: message.timestamp, timestamp
variable, formatTimestamp) and implement the guard or conditional formatting
there.

In `@src/commands/spaces/cursors/set.ts`:
- Around line 49-72: Update the duration flag help text and runtime handling so
0 means run indefinitely (consistent with other long-running commands): change
the Flags.integer description for duration to "Automatically exit after N
seconds (0 = run indefinitely)" (it already has char: "D"), and modify the logic
that currently treats flags.duration === 0 as "exit immediately" (the check
around flags.duration in this command's run loop / the block referenced at lines
~394-401) to instead treat 0 as no timeout (i.e., skip automatic exit when
duration === 0) so behavior and help text are aligned.
- Around line 110-153: After parsing flags.data into cursorData (and after
building cursorData from flags.x/flags.y plus parsed additionalData), validate
that cursorData.position exists and that position.x and position.y are numbers;
if validation fails or no position was supplied in the flags.data-only path,
return the same JSON-formatted error when shouldOutputJson(flags) is true by
calling this.jsonError({ error: <message>, success: false }, flags) and
otherwise call this.error(<message>); update the code paths around the
JSON.parse success in the handlers that set cursorData (referencing jsonError,
shouldOutputJson, cursorData, flags, and this.error) to produce consistent JSON
output for missing or incorrectly typed position data.
- Around line 105-116: The parsed --data value must be validated as a plain
object before assigning to cursorData.data (and the other similar assignment
block that sets cursorUpdate.data); after JSON.parse(flags.data) check that the
result is !== null, typeof result === 'object' and !Array.isArray(result) and
only then set cursorData.data (or cursorUpdate.data); if the check fails, reuse
the existing error handling path (this.shouldOutputJson => this.jsonError(...)
else this.error(...)) with the same "Invalid JSON in --data flag..." message so
primitives, arrays, nulls and other non-object JSON are rejected.

In `@src/commands/stats/account.ts`:
- Around line 159-166: The cleanup function unconditionally calls
this.log("\nUnsubscribed from live stats"), which can corrupt machine-readable
output; modify cleanup in the same block (the cleanup arrow function that clears
this.pollInterval) to only emit that human-readable message when not running in
JSON mode (check the command's JSON flag/property, e.g. this.json or a --json
option) — otherwise suppress the log so only valid JSON remains.
- Around line 197-207: The catch block calls this.error() which throws, making
clearInterval(this.pollInterval) unreachable; move the cleanup before throwing
or ensure cleanup always runs by clearing the interval at the top of the catch
block (i.e., call if (this.pollInterval) clearInterval(this.pollInterval);
before invoking this.jsonError or this.error in the catch inside the method
where the try/catch currently lives), or alternatively capture the error,
perform cleanup, then rethrow or call this.error; reference this.error,
this.jsonError, and this.pollInterval when applying the change.

In `@src/commands/stats/app.ts`:
- Around line 177-184: The cleanup() function prints a plain-text shutdown
message via this.log which corrupts JSON-only output; change it to either call
this.log only when shouldOutputJson() is false or send the message to stderr
(e.g., use this.logError or process.stderr.write) so that when
shouldOutputJson() is true no plain text is written. Update the block that
clears this.pollInterval and the final this.log("\nUnsubscribed from live
stats") to be conditional on shouldOutputJson() (or routed to stderr) to keep
JSON stdout machine-readable.
- Around line 37-44: The live mode currently accepts --start/--end from
timeRangeFlags but fetchAndDisplayStats() ignores them and always uses the last
24 hours; update the command so live mode either rejects start/end or respects
them: inside the command's run() (or the flag-parsing path that handles the live
boolean) add validation that if flags.live is true and either flags.start or
flags.end is provided you either throw a usage error (fail fast) or pass those
parsed start/end values into the live polling call, making
fetchAndDisplayStats() (or the live fetch helper it calls) accept optional
start/end parameters and use them instead of the default last-24-hours range
when provided; reference the static flags (timeRangeFlags), the flags.live
boolean, and fetchAndDisplayStats() to locate where to add validation or
parameter threading.

---

Nitpick comments:
In @.claude/CLAUDE.md:
- Line 127: Update the documentation for timeRangeFlags to show a more
comprehensive set of relative time examples supported by parseTimestamp() in
src/utils/time.ts (e.g., "30s", "1m", "5m", "30m", "1h", "1d", "2d", "1w") so
readers clearly see seconds/minutes/hours/days/weeks are accepted; edit the line
describing **`timeRangeFlags`** (the `--start`, `--end` examples) to include
these additional relative formats.

In `@src/commands/apps/set-apns-p12.ts`:
- Around line 57-59: The progress message logged by this.log(progress(...)) does
not end with an ellipsis; update the string passed to progress in the
set-apns-p12 command so it ends with "..." (e.g. change `Uploading APNS P12
certificate for app ${resource(args.id)}` to `Uploading APNS P12 certificate for
app ${resource(args.id)}...`). Locate the call to progress(...) inside the
command (the this.log(progress(...)) invocation) and append the three dots to
the template literal so the progress helper output follows the coding guideline.

In `@src/commands/integrations/delete.ts`:
- Around line 92-116: The non-JSON output in the delete command prints field
labels as plain text—wrap the secondary labels (the static parts in the this.log
calls that print `ID:`, `App ID:`, `Type:`, `Source Type:`) with chalk.dim() for
consistent styling; add an import for chalk at the top of the file and update
the four this.log(...) lines in the else branch (the block guarded by
this.shouldOutputJson(flags) and using success(), resource(), and
formatJsonOutput) so each static label is formatted via chalk.dim() while
keeping the dynamic values unchanged.

In `@src/commands/logs/connection-lifecycle/subscribe.ts`:
- Around line 22-26: The duration flag definition (duration: Flags.integer) has
an incomplete description; update its description to "Automatically exit after N
seconds (0 = run indefinitely)" and ensure the alias char remains "D" (alias -D)
in the Flags.integer call so the flag matches coding guidelines; locate the
duration flag in subscribe.ts and replace the current description string
accordingly.
- Around line 106-110: The secondary label "Data:" in the message handling of
the subscribe command is using chalk.green(); change it to chalk.dim() so
secondary labels follow the project's style guide—locate the block that checks
message.data !== null && message.data !== undefined inside the subscribe handler
and replace chalk.green("Data:") with chalk.dim("Data:") in the this.log call
that JSON.stringifies message.data.

In `@src/commands/logs/push/history.ts`:
- Line 2: Replace the runtime import of Ably with a type-only import since the
symbol Ably is used only for type annotations; change the import statement that
currently reads `import * as Ably from "ably";` to a type-only import (e.g.,
using `import type`) so the module is not included at runtime and ESLint
consistent-type-imports rules are satisfied, then run the linter to verify
(refer to the Ably symbol in this file when making the change).

In `@src/commands/rooms/messages/history.ts`:
- Around line 67-69: The code calls this.error("Failed to create Chat client")
which in oclif throws and never returns, so remove the redundant unreachable
return statement immediately after it (in the same block where the Chat client
is created), leaving only the this.error(...) call; locate the occurrence in the
history command handler (the method containing the Chat client creation) and
delete the trailing return to clean up unreachable code.

In `@src/commands/support/ask.ts`:
- Around line 38-43: Replace the ad-hoc ora spinner and manual
this.log("Thinking...") with the shared progress helper: remove the ora creation
and the conditional this.log call in the support ask command and instead call
progress("Thinking...") where the current spinner logic runs (respecting the
same isInteractive / shouldOutputJson(flags) condition); ensure you import or
reference the progress() helper and use its returned handle/stop behavior
consistent with other commands so the in-progress UI matches the shared UX.

In `@test/unit/commands/queues/delete.test.ts`:
- Around line 421-423: The skipped assertions in delete.test.ts still expect the
old output format ("Queue \"${mockQueueName}\"" and "deleted successfully")
which now conflicts with active tests that assert "Queue deleted:"; update the
skipped test expectations to match the new format by changing the expect calls
that reference stdout and mockQueueName to assert the "Queue deleted:" style
(use the same stdout contains checks as the active tests) so that if the tests
are unskipped they will pass consistently with the current output formatting.

In `@test/unit/utils/time.test.ts`:
- Around line 83-88: The test "should trim whitespace from relative input" uses
vi.useFakeTimers() but may leave fake timers enabled if the assertion throws;
wrap the fake-timer setup and assertion in a try/finally (or move to a nested
beforeEach/afterEach) so vi.useRealTimers() always runs. Specifically, in the
test containing parseTimestamp(" 1h "), ensure vi.useRealTimers() is called in a
finally block (or ensure the whitespace suite uses beforeEach to call
vi.useFakeTimers() and afterEach to call vi.useRealTimers()) so timers are
always restored even on assertion failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b8786d4c-3330-4fbe-b856-32088439d9b3

📥 Commits

Reviewing files that changed from the base of the PR and between 0794c34 and ca4c3c7.

📒 Files selected for processing (81)
  • .claude/CLAUDE.md
  • .cursor/rules/AI-Assistance.mdc
  • eslint.config.js
  • src/commands/accounts/login.ts
  • src/commands/apps/channel-rules/create.ts
  • src/commands/apps/channel-rules/delete.ts
  • src/commands/apps/channel-rules/list.ts
  • src/commands/apps/create.ts
  • src/commands/apps/delete.ts
  • src/commands/apps/set-apns-p12.ts
  • src/commands/apps/update.ts
  • src/commands/auth/keys/create.ts
  • src/commands/bench/subscriber.ts
  • src/commands/channels/batch-publish.ts
  • src/commands/channels/history.ts
  • src/commands/channels/list.ts
  • src/commands/channels/occupancy.ts
  • src/commands/channels/occupancy/get.ts
  • src/commands/channels/occupancy/subscribe.ts
  • src/commands/channels/presence/enter.ts
  • src/commands/channels/presence/subscribe.ts
  • src/commands/channels/publish.ts
  • src/commands/channels/subscribe.ts
  • src/commands/integrations/delete.ts
  • src/commands/interactive.ts
  • src/commands/logs/channel-lifecycle/subscribe.ts
  • src/commands/logs/connection-lifecycle/history.ts
  • src/commands/logs/connection-lifecycle/subscribe.ts
  • src/commands/logs/history.ts
  • src/commands/logs/push/history.ts
  • src/commands/logs/push/subscribe.ts
  • src/commands/logs/subscribe.ts
  • src/commands/queues/delete.ts
  • src/commands/rooms/list.ts
  • src/commands/rooms/messages/history.ts
  • src/commands/rooms/messages/reactions/remove.ts
  • src/commands/rooms/messages/reactions/send.ts
  • src/commands/rooms/messages/reactions/subscribe.ts
  • src/commands/rooms/messages/send.ts
  • src/commands/rooms/messages/subscribe.ts
  • src/commands/rooms/occupancy/get.ts
  • src/commands/rooms/occupancy/subscribe.ts
  • src/commands/rooms/presence/enter.ts
  • src/commands/rooms/presence/subscribe.ts
  • src/commands/rooms/reactions/send.ts
  • src/commands/rooms/reactions/subscribe.ts
  • src/commands/rooms/typing/keystroke.ts
  • src/commands/rooms/typing/subscribe.ts
  • src/commands/spaces/cursors/get-all.ts
  • src/commands/spaces/cursors/set.ts
  • src/commands/spaces/cursors/subscribe.ts
  • src/commands/spaces/list.ts
  • src/commands/spaces/locations.ts
  • src/commands/spaces/locations/get-all.ts
  • src/commands/spaces/locations/set.ts
  • src/commands/spaces/locations/subscribe.ts
  • src/commands/spaces/locks/acquire.ts
  • src/commands/spaces/locks/get-all.ts
  • src/commands/spaces/locks/get.ts
  • src/commands/spaces/locks/subscribe.ts
  • src/commands/spaces/members/enter.ts
  • src/commands/spaces/members/subscribe.ts
  • src/commands/stats/account.ts
  • src/commands/stats/app.ts
  • src/commands/support/ask.ts
  • src/flags.ts
  • src/services/history-manager.ts
  • src/utils/time.ts
  • test/e2e/interactive/ctrl-c-behavior.test.ts
  • test/e2e/spaces/spaces-e2e.test.ts
  • test/unit/commands/apps/set-apns-p12.test.ts
  • test/unit/commands/channels/batch-publish.test.ts
  • test/unit/commands/channels/history.test.ts
  • test/unit/commands/did-you-mean.test.ts
  • test/unit/commands/interactive-sigint.test.ts
  • test/unit/commands/interactive.test.ts
  • test/unit/commands/queues/delete.test.ts
  • test/unit/commands/rooms/messages.test.ts
  • test/unit/commands/stats/account.test.ts
  • test/unit/commands/stats/app.test.ts
  • test/unit/utils/time.test.ts
💤 Files with no reviewable changes (3)
  • src/commands/interactive.ts
  • test/e2e/spaces/spaces-e2e.test.ts
  • test/e2e/interactive/ctrl-c-behavior.test.ts
🚧 Files skipped from review as they are similar to previous changes (38)
  • src/commands/channels/subscribe.ts
  • src/commands/apps/channel-rules/list.ts
  • src/commands/channels/occupancy.ts
  • src/commands/logs/connection-lifecycle/history.ts
  • src/commands/spaces/locations.ts
  • src/commands/apps/channel-rules/delete.ts
  • src/commands/channels/list.ts
  • .cursor/rules/AI-Assistance.mdc
  • src/commands/rooms/messages/reactions/send.ts
  • src/commands/rooms/occupancy/subscribe.ts
  • src/commands/apps/channel-rules/create.ts
  • src/commands/queues/delete.ts
  • src/commands/rooms/messages/reactions/remove.ts
  • src/commands/channels/batch-publish.ts
  • src/commands/rooms/messages/reactions/subscribe.ts
  • src/commands/rooms/typing/subscribe.ts
  • src/commands/spaces/locks/get.ts
  • src/commands/rooms/messages/subscribe.ts
  • src/commands/spaces/locks/subscribe.ts
  • src/commands/apps/delete.ts
  • src/commands/apps/update.ts
  • test/unit/commands/did-you-mean.test.ts
  • test/unit/commands/interactive-sigint.test.ts
  • src/commands/spaces/locations/subscribe.ts
  • src/commands/logs/subscribe.ts
  • src/commands/bench/subscriber.ts
  • src/commands/spaces/locks/acquire.ts
  • src/flags.ts
  • src/utils/time.ts
  • src/commands/spaces/cursors/subscribe.ts
  • src/commands/channels/occupancy/subscribe.ts
  • src/commands/channels/presence/subscribe.ts
  • src/commands/spaces/locks/get-all.ts
  • test/unit/commands/channels/history.test.ts
  • src/commands/channels/publish.ts
  • src/commands/spaces/list.ts
  • src/commands/rooms/messages/send.ts
  • src/commands/apps/create.ts

- Add inverted time range validation (--start > --end) to all history commands
- Add clientIdFlag to rooms/occupancy/get, rooms/reactions/subscribe, rooms/typing/keystroke
- Fix current.member serialization in spaces/locations/get-all JSON output
- Handle partial time ranges in stats/account (default missing bound)
- Fix misleading comments in interactive test and cursors/get-all

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@sacOO7 sacOO7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@umair-ably umair-ably merged commit 910f9c4 into main Mar 6, 2026
10 checks passed
@umair-ably umair-ably deleted the DX-155-start-end-params branch March 6, 2026 06:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants